180 lines
4.7 KiB
Rust
180 lines
4.7 KiB
Rust
#![no_std]
|
|
#![forbid(unsafe_code)]
|
|
|
|
pub use int_enum::{IntEnum, IntEnumError};
|
|
|
|
pub mod hash_table;
|
|
pub mod hilbert;
|
|
|
|
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,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
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,
|
|
}
|
|
|
|
pub fn trunc_key_at0(key: &[u8]) -> &[u8] {
|
|
let key_end = key.iter().take_while(|&&i| i != 0).count();
|
|
&key[..key_end]
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct LinearTableEntry<R> {
|
|
pub name: R,
|
|
pub typ: u32,
|
|
pub location: u32,
|
|
pub meta: u32,
|
|
pub rest: R,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct LinearTableRef<'a> {
|
|
cklen: usize,
|
|
strtab: &'a [u8],
|
|
data: &'a [u8],
|
|
}
|
|
|
|
pub fn decode_location(location: u32) -> usize {
|
|
16 * usize::try_from(location).unwrap()
|
|
}
|
|
|
|
impl<'a> LinearTableRef<'a> {
|
|
pub fn parse(data: &'a [u8], location: u32) -> Option<Self> {
|
|
let sel = decode_location(location);
|
|
if data.len() < (sel + 16) {
|
|
return None;
|
|
}
|
|
let sel = &data[sel..];
|
|
let strtab_link = u32::from_be_bytes(sel[0..4].try_into().unwrap());
|
|
let entcount = u16::from_be_bytes(sel[4..6].try_into().unwrap());
|
|
let entsize = u16::from_be_bytes(sel[6..8].try_into().unwrap());
|
|
let strtab_loc = decode_location(strtab_link);
|
|
let dataspan = 16..(16 + 16 * usize::from(entsize) * usize::from(entcount));
|
|
|
|
if data.len() <= strtab_loc || entsize == 0 {
|
|
return None;
|
|
}
|
|
|
|
Some(Self {
|
|
cklen: 16 * usize::from(entsize),
|
|
strtab: &data[strtab_loc..],
|
|
data: sel.get(dataspan)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for LinearTableRef<'a> {
|
|
type Item = LinearTableEntry<&'a [u8]>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.data.len() < self.cklen {
|
|
debug_assert!(self.data.is_empty());
|
|
return None;
|
|
}
|
|
let (i, j) = self.data.split_at(self.cklen);
|
|
self.data = j;
|
|
let name = usize::try_from(u32::from_be_bytes(i[0..4].try_into().unwrap())).unwrap();
|
|
let name = trunc_key_at0(self.strtab.get(name..)?);
|
|
Some(LinearTableEntry {
|
|
name,
|
|
typ: u32::from_be_bytes(i[4..8].try_into().unwrap()),
|
|
location: u32::from_be_bytes(i[8..12].try_into().unwrap()),
|
|
meta: u32::from_be_bytes(i[12..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]) -> Option<Self> {
|
|
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)?;
|
|
Some(Self {
|
|
xybits,
|
|
data: data.get(16..16 + exp_len)?,
|
|
})
|
|
}
|
|
|
|
pub fn lookup(&self, x: u8, y: u8) -> (u64, u64) {
|
|
let xybr = 8 - self.xybits;
|
|
let (x, y) = (x >> xybr, y >> xybr);
|
|
let loc = decode_location(hilbert::xy2d(2 * self.xybits, x.into(), y.into()));
|
|
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()),
|
|
)
|
|
}
|
|
}
|