144 lines
3.6 KiB
Rust
144 lines
3.6 KiB
Rust
#![no_std]
|
|
#![forbid(unsafe_code)]
|
|
|
|
use int_enum::IntEnum;
|
|
|
|
pub mod hilbert;
|
|
|
|
mod ht;
|
|
pub use ht::*;
|
|
|
|
pub const MAGIC: [u8; 4] = [b'Y', b'g', b'L', b'n'];
|
|
|
|
#[derive(Clone, Copy, Debug, IntEnum)]
|
|
#[repr(u32)]
|
|
#[rustfmt::skip]
|
|
pub enum FileType {
|
|
None = 0x0000_0000,
|
|
Text = 0x0000_0001,
|
|
}
|
|
|
|
pub struct FileHeader {
|
|
pub magic: [u8; 4],
|
|
pub generator: u32,
|
|
pub typ: u32,
|
|
pub version: u32,
|
|
}
|
|
|
|
impl FileHeader {
|
|
pub fn decode(data: [u8; 16]) -> Self {
|
|
Self {
|
|
magic: data[0..4].try_into().unwrap(),
|
|
generator: u32::from_be_bytes(data[4..8].try_into().unwrap()),
|
|
typ: u32::from_be_bytes(data[8..12].try_into().unwrap()),
|
|
version: u32::from_be_bytes(data[12..16].try_into().unwrap()),
|
|
}
|
|
}
|
|
|
|
pub fn encode(&self) -> [u8; 16] {
|
|
let mut data = [0u8; 16];
|
|
data[0..4].copy_from_slice(&self.magic);
|
|
data[4..8].copy_from_slice(&u32::to_be_bytes(self.generator));
|
|
data[8..12].copy_from_slice(&u32::to_be_bytes(self.typ));
|
|
data[12..16].copy_from_slice(&u32::to_be_bytes(self.version));
|
|
data
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, IntEnum)]
|
|
#[repr(u32)]
|
|
#[rustfmt::skip]
|
|
pub enum Type {
|
|
PlainText = 0x0000_0000,
|
|
NestedText = 0x0000_0001,
|
|
|
|
StringTable = 0x0000_0010,
|
|
LinearPlain = 0x0000_0012,
|
|
|
|
HashPlain = 0x0000_0020,
|
|
HashLink = 0x0000_0021,
|
|
|
|
X2dhcPlain = 0x0000_0030,
|
|
X2dhcLink = 0x0000_0031,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, IntEnum)]
|
|
#[repr(u16)]
|
|
#[rustfmt::skip]
|
|
pub enum Ntt01 {
|
|
Div = 0x0000,
|
|
Group = 0x0001,
|
|
Header = 0x0002,
|
|
Quote = 0x0003,
|
|
Code = 0x0004,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct LinearTableEntry<R> {
|
|
pub name: u32,
|
|
pub typ: u32,
|
|
pub location: u32,
|
|
pub entsize: u16,
|
|
pub entcount: u16,
|
|
pub rest: R,
|
|
}
|
|
|
|
pub fn linear_table_iter(
|
|
data: &[u8],
|
|
entsize: u16,
|
|
entcount: u16,
|
|
) -> impl Iterator<Item = LinearTableEntry<&'_ [u8]>> {
|
|
assert!(entsize > 0);
|
|
data.chunks(usize::from(entsize) * 16)
|
|
.take(entcount.into())
|
|
.map(|i| LinearTableEntry {
|
|
name: u32::from_be_bytes(i[0..4].try_into().unwrap()),
|
|
typ: u32::from_be_bytes(i[4..8].try_into().unwrap()),
|
|
location: u32::from_be_bytes(i[8..12].try_into().unwrap()),
|
|
entsize: u16::from_be_bytes(i[12..14].try_into().unwrap()),
|
|
entcount: u16::from_be_bytes(i[14..16].try_into().unwrap()),
|
|
rest: &i[16..],
|
|
})
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct X2dhc<'a> {
|
|
xybits: u8,
|
|
data: &'a [u8],
|
|
}
|
|
|
|
impl<'a> X2dhc<'a> {
|
|
pub fn parse(data: &'a [u8], entsize: u16, entcount: u16) -> Option<Self> {
|
|
if entsize != 1 {
|
|
return None;
|
|
}
|
|
let data = data.get(..(16 * usize::from(entcount)))?;
|
|
if data.len() < 32 {
|
|
return None;
|
|
}
|
|
let xybits = data[0];
|
|
if xybits >= 8 {
|
|
return None;
|
|
}
|
|
let exp_len = 1usize.checked_shl(xybits.into())?.checked_mul(16)?;
|
|
if (data.len() - 16) != exp_len {
|
|
return None;
|
|
}
|
|
Some(Self {
|
|
xybits,
|
|
data: &data[16..],
|
|
})
|
|
}
|
|
|
|
pub fn lookup(&self, x: u8, y: u8) -> (u64, u64) {
|
|
let xybr = 8 - self.xybits;
|
|
let (x, y) = (x >> xybr, y >> xybr);
|
|
let loc = 16 * usize::try_from(hilbert::xy2d(2 * self.xybits, x.into(), y.into())).unwrap();
|
|
let dloc = &self.data[loc..loc + 16];
|
|
(
|
|
u64::from_be_bytes(dloc[0..8].try_into().unwrap()),
|
|
u64::from_be_bytes(dloc[8..16].try_into().unwrap()),
|
|
)
|
|
}
|
|
}
|