196 lines
4.9 KiB
Rust
196 lines
4.9 KiB
Rust
use std::fmt;
|
|
|
|
pub struct ReadError {
|
|
pub msg: String,
|
|
pub stack: Vec<ParseFrame>,
|
|
}
|
|
|
|
impl fmt::Debug for ReadError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let stack = self.stack.iter().map(|pf| format!("{}@{:x}", pf.name, pf.loc)).collect::<Vec<String>>().join("->");
|
|
f.debug_struct("ReadError")
|
|
.field("msg", &self.msg)
|
|
.field("stack", &stack)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ParseFrame {
|
|
pub name: String,
|
|
pub loc: usize,
|
|
}
|
|
|
|
pub type ReadResult<T> = Result<T, ReadError>;
|
|
|
|
pub struct GMFElementDescriptor {
|
|
fields: Vec<GMFElementField>,
|
|
}
|
|
|
|
pub struct GMFElementField {
|
|
rust_name: &'static str,
|
|
gma_repr: GMARepr,
|
|
}
|
|
|
|
pub enum GMARepr {
|
|
Named(&'static str),
|
|
}
|
|
|
|
pub trait GMFElement: Sized {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self>;
|
|
//fn descriptor() -> GMFElementDescriptor;
|
|
}
|
|
|
|
impl<T: GMFElement> GMFElement for Vec<T> {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
let count: u32 = r.read("count")?;
|
|
(0..count).map(|_| r.read("data")).collect()
|
|
}
|
|
}
|
|
|
|
impl<T: GMFElement, const N: usize> GMFElement for [T; N] {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
(0..N).map(|_| r.read("data")).collect::<ReadResult<Vec<T>>>()?.try_into().map_err(|_| r.error("eof"))
|
|
}
|
|
}
|
|
|
|
|
|
impl<T: GMFElement> GMFElement for Option<T> {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
let count: u32 = r.read("count")?;
|
|
if count != 0 {
|
|
return Ok(Some(r.read("data")?));
|
|
}
|
|
return Ok(None);
|
|
}
|
|
}
|
|
|
|
impl GMFElement for u8 {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
Ok(r.bytes(1)?[0])
|
|
}
|
|
}
|
|
|
|
impl GMFElement for u32 {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
|
Ok(u32::from_le_bytes(buf))
|
|
}
|
|
}
|
|
|
|
impl GMFElement for f32 {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
|
Ok(f32::from_le_bytes(buf))
|
|
}
|
|
}
|
|
|
|
impl GMFElement for String {
|
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
|
let len: u32 = r.read("len")?;
|
|
r.push("bytes".to_string());
|
|
let bytes = r.bytes(len as usize)?;
|
|
let bytes: Vec<u8> = bytes.into_iter().filter(|&b| b != 0).collect();
|
|
let res = std::str::from_utf8(&bytes).map_err(|_| r.error("invalid string")).map(String::from);
|
|
r.pop();
|
|
res
|
|
}
|
|
}
|
|
|
|
pub struct ReadStream<R: std::io::Read> {
|
|
backing: R,
|
|
stack: Vec<ParseFrame>,
|
|
pos: usize,
|
|
}
|
|
|
|
impl<R: std::io::Read> ReadStream<R> {
|
|
pub fn new(r: R) -> Self {
|
|
Self {
|
|
backing: r,
|
|
pos: 0,
|
|
stack: vec![
|
|
ParseFrame{name: "root".into(), loc: 0},
|
|
],
|
|
}
|
|
}
|
|
|
|
//fn context(&mut self, s: String) {
|
|
// self.stack.last_mut().unwrap().name = s;
|
|
// self.stack.last_mut().unwrap().loc = self.pos;
|
|
//}
|
|
|
|
fn push(&mut self, s: String) {
|
|
self.stack.push(ParseFrame {
|
|
name: s,
|
|
loc: self.pos,
|
|
});
|
|
}
|
|
|
|
fn pop(&mut self) {
|
|
self.stack.pop();
|
|
}
|
|
}
|
|
|
|
impl <R: std::io::Read> ReadStream<R> {
|
|
pub fn bytes(&mut self, n: usize) -> ReadResult<Vec<u8>> {
|
|
let mut buf: Vec<u8> = vec![0; n];
|
|
self.pos += n;
|
|
match self.backing.read(&mut buf) {
|
|
Ok(_) => return Ok(buf),
|
|
Err(_) => return Err(self.error("eof")),
|
|
}
|
|
}
|
|
|
|
pub fn tlv(&mut self) -> ReadResult<TLV> {
|
|
let start_pos = self.pos;
|
|
let tag: u32 = self.read("tag")?;
|
|
let flags: u32 = self.read("flags")?;
|
|
let length: u32 = self.read("length")?;
|
|
Ok(TLV {
|
|
tag, flags, length, start_pos,
|
|
})
|
|
}
|
|
|
|
pub fn read<T: GMFElement>(&mut self, ctx: &str) -> ReadResult<T> {
|
|
self.push(ctx.to_string());
|
|
let res = T::read(self);
|
|
self.pop();
|
|
res
|
|
}
|
|
|
|
pub fn error<S: std::string::ToString>(&self, msg: S) -> ReadError {
|
|
ReadError {
|
|
msg: msg.to_string(),
|
|
stack: self.stack.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct TLV {
|
|
pub tag: u32,
|
|
pub flags: u32,
|
|
pub length: u32,
|
|
|
|
start_pos: usize,
|
|
}
|
|
|
|
impl TLV {
|
|
pub fn error(&self, msg: &str) -> ReadError {
|
|
ReadError {
|
|
msg: msg.to_string(),
|
|
stack: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn check<R: std::io::Read>(&self, r: &mut ReadStream<R>) -> ReadResult<()> {
|
|
let expected = self.start_pos + self.length as usize + 12usize;
|
|
if expected != r.pos {
|
|
return Err(ReadError {
|
|
msg: format!("tlv length mismatch: expected end at {}, got at {}", expected, r.pos),
|
|
stack: vec![],
|
|
});
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|