espers 0.1.0

Library for reading ESP and ESM files
Documentation
use crate::error::Error;
use binrw::{binrw, BinRead};
use serde_derive::{Deserialize, Serialize};
use std::io::Cursor;

#[binrw]
#[brw(little, magic = b"VMAD")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VMAD {
    pub size: u16,
    #[br(count = size)]
    pub data: Vec<u8>,
}

#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PropertyType {
    #[brw(magic = 1u8)]
    Type1 { status: u8, data: [u32; 2] },
    #[brw(magic = 3u8)]
    Type2 { status: u8, data: u32 },
    #[brw(magic = 5u8)]
    Type3 { status: u8, data: u8 },
}

#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawProperty {
    pub name_size: u16,
    #[br(count = name_size)]
    pub name: Vec<u8>,

    pub kind: PropertyType,
}

#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawScript {
    pub name_size: u16,
    #[br(count = name_size)]
    pub name: Vec<u8>,

    pub status: u8,
    pub property_count: u16,

    #[br(count = property_count)]
    pub properties: Vec<RawProperty>,
}

#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawScriptList {
    pub version: u16,
    pub object_format: u16,
    pub script_count: u16,

    #[br(count = script_count)]
    pub scripts: Vec<RawScript>,
}

impl TryFrom<VMAD> for RawScriptList {
    type Error = Error;

    fn try_from(raw: VMAD) -> Result<RawScriptList, Self::Error> {
        let mut cursor = Cursor::new(&raw.data);
        Ok(RawScriptList::read(&mut cursor)?)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Property {
    pub name: String,
    pub kind: PropertyType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Script {
    pub name: String,
    pub status: u8,
    pub properties: Vec<Property>,
}

impl TryInto<Script> for RawScript {
    type Error = Error;

    fn try_into(self) -> Result<Script, Self::Error> {
        Ok(Script {
            name: String::from_utf8_lossy(&self.name).into(),
            status: self.status,
            properties: self
                .properties
                .into_iter()
                .map(|p| Property {
                    name: String::from_utf8_lossy(&p.name).into(),
                    kind: p.kind,
                })
                .collect(),
        })
    }
}

impl TryInto<Vec<Script>> for VMAD {
    type Error = Error;

    fn try_into(self) -> Result<Vec<Script>, Self::Error> {
        let mut cursor = Cursor::new(&self.data);
        let scripts = RawScriptList::read(&mut cursor)?;
        scripts.scripts.into_iter().map(TryInto::try_into).collect()
    }
}