gmftools/gmflib/src/machinery.rs

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(())
}
}