aamp 0.1.6

Rust library for Nintendo parameter archive (AAMP) files
Documentation
use super::types;
use super::{Parameter, ParameterIO, ParameterList, ParameterObject};
use binread::{BinRead, NullString};
use indexmap::IndexMap;
use std::convert::TryFrom;
use std::io::{Read, Seek, SeekFrom};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ParseError {
    #[error(transparent)]
    BinReadError(#[from] binread::error::Error),
    #[error(transparent)]
    YamlParseError(#[from] crate::yaml::parse::YamlParseError),
    #[error(transparent)]
    IOError(#[from] std::io::Error),
}

#[derive(Debug, BinRead)]
enum ParameterType {
    Bool = 0,
    F32,
    Int,
    Vec2,
    Vec3,
    Vec4,
    Color,
    String32,
    String64,
    Curve1,
    Curve2,
    Curve3,
    Curve4,
    BufferInt,
    BufferF32,
    String256,
    Quat,
    U32,
    BufferU32,
    BufferBinary,
    StringRef,
}

impl TryFrom<u8> for ParameterType {
    type Error = String;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(ParameterType::Bool),
            1 => Ok(ParameterType::F32),
            2 => Ok(ParameterType::Int),
            3 => Ok(ParameterType::Vec2),
            4 => Ok(ParameterType::Vec3),
            5 => Ok(ParameterType::Vec4),
            6 => Ok(ParameterType::Color),
            7 => Ok(ParameterType::String32),
            8 => Ok(ParameterType::String64),
            9 => Ok(ParameterType::Curve1),
            10 => Ok(ParameterType::Curve2),
            11 => Ok(ParameterType::Curve3),
            12 => Ok(ParameterType::Curve4),
            13 => Ok(ParameterType::BufferInt),
            14 => Ok(ParameterType::BufferF32),
            15 => Ok(ParameterType::String256),
            16 => Ok(ParameterType::Quat),
            17 => Ok(ParameterType::U32),
            18 => Ok(ParameterType::BufferU32),
            19 => Ok(ParameterType::BufferBinary),
            20 => Ok(ParameterType::StringRef),
            _ => Err(format!("Invalid parameter type: {}", value)),
        }
    }
}

#[derive(BinRead, Debug)]
#[br(little, assert(version == 2 && flags & 1 == 1))]
struct ParseHeader {
    version: u32,
    flags: u32,
    file_size: u32,
    pio_version: u32,
    pio_offset: u32,
    num_lists: u32,
    num_objects: u32,
    num_params: u32,
    data_section_size: u32,
    string_section_size: u32,
    idk_section_size: u32,
}

#[derive(BinRead, Debug)]
#[br(little)]
struct ParseParameterList {
    crc: u32,
    lists_rel_offset: u16,
    num_lists: u16,
    objs_rel_offset: u16,
    num_objs: u16,
}

#[derive(BinRead, Debug)]
#[br(little)]
struct ParseParameterObject {
    crc: u32,
    params_rel_offset: u16,
    num_params: u16,
}

#[derive(BinRead, Debug)]
#[br(little)]
struct ParseParameter {
    crc: u32,
    #[br(map = |x: [u8; 3]| u32::from_le_bytes([x[0], x[1], x[2], 0]))]
    data_offset: u32,
    #[br(map = |x: u8| ParameterType::try_from(x).expect(&format!("Invalid type for param {}", crc)))]
    param_type: ParameterType,
}

#[derive(BinRead, Debug)]
#[br(little, magic = b"AAMP")]
struct ParseParameterIO {
    header: ParseHeader,
    pio_type: NullString,
}

#[derive(BinRead, Debug)]
struct ParseBufferInt {
    size: u32,
    #[br(count=size)]
    content: Vec<i32>,
}

#[derive(BinRead, Debug)]
struct ParseBufferF32 {
    size: u32,
    #[br(count=size)]
    content: Vec<f32>,
}

#[derive(BinRead, Debug)]
struct ParseBufferU32 {
    size: u32,
    #[br(count=size)]
    content: Vec<u32>,
}

#[derive(BinRead, Debug)]
struct ParseBufferBinary {
    size: u32,
    #[br(count=size)]
    content: Vec<u8>,
}

impl ParameterIO {
    /// Parses an AAMP Parameter IO document from its binary format. Takes any reader with the
    /// Read and Seek traits and returns a result containing a `ParameterIO` or a `ParseError`.
    pub fn from_binary<R: Read + Seek>(reader: &mut R) -> Result<ParameterIO, ParseError> {
        let ppio: ParseParameterIO = ParseParameterIO::read(reader)?;
        reader.seek(SeekFrom::Start((ppio.header.pio_offset + 0x30) as u64))?;
        let parse_pio: ParseParameterList = ParseParameterList::read(reader)?;
        let param_root: ParameterList =
            ParameterList::from_parse_list(parse_pio, ppio.header.pio_offset + 0x30, reader)?;
        let pio = ParameterIO {
            version: ppio.header.pio_version,
            pio_type: ppio.pio_type.to_string(),
            lists: param_root.lists,
            objects: param_root.objects,
        };
        Ok(pio)
    }
}

impl ParameterList {
    fn from_parse_list<R: Read + Seek>(
        plist: ParseParameterList,
        offset: u32,
        reader: &mut R,
    ) -> Result<ParameterList, ParseError> {
        let mut list_map: IndexMap<u32, ParameterList> = IndexMap::new();
        let mut obj_map: IndexMap<u32, ParameterObject> = IndexMap::new();
        if plist.num_lists > 0 {
            for i in 0..plist.num_lists {
                let off = offset + (plist.lists_rel_offset as u32 * 4) + (12 * i as u32);
                reader.seek(SeekFrom::Start(off as u64))?;
                let list: ParseParameterList = ParseParameterList::read(reader)?;
                list_map.insert(list.crc, ParameterList::from_parse_list(list, off, reader)?);
            }
        }
        if plist.num_objs > 0 {
            for i in 0..plist.num_objs {
                let off = offset + (plist.objs_rel_offset as u32 * 4) + (8 * i as u32);
                reader.seek(SeekFrom::Start(off as u64))?;
                let obj: ParseParameterObject = ParseParameterObject::read(reader)?;
                obj_map.insert(obj.crc, ParameterObject::from_parse_obj(obj, off, reader)?);
            }
        }
        Ok(ParameterList {
            lists: list_map,
            objects: obj_map,
        })
    }
}

impl ParameterObject {
    fn from_parse_obj<R: Read + Seek>(
        pobj: ParseParameterObject,
        offset: u32,
        reader: &mut R,
    ) -> Result<ParameterObject, ParseError> {
        let mut param_map: IndexMap<u32, Parameter> = IndexMap::new();
        if pobj.num_params > 0 {
            for i in 0..pobj.num_params {
                let off = offset + (pobj.params_rel_offset as u32 * 4) + (8 * i as u32);
                reader.seek(SeekFrom::Start(off as u64))?;
                let param: ParseParameter = ParseParameter::read(reader)?;
                param_map.insert(
                    param.crc,
                    Parameter::from_parse_param(param, off as u32, reader)?,
                );
            }
        }
        Ok(ParameterObject(param_map))
    }
}

fn add_parsed_string_to_table(string: &str) {
    let mut table = crate::names::TABLE.lock().unwrap();
    table.add_name(string);
}

impl Parameter {
    fn from_parse_param<R: Read + Seek>(
        param: ParseParameter,
        offset: u32,
        reader: &mut R,
    ) -> Result<Parameter, ParseError> {
        let data_offset = offset as u64 + (param.data_offset as u64 * 4);
        reader.seek(SeekFrom::Start(data_offset))?;
        match param.param_type {
            ParameterType::Bool => Ok(Parameter::Bool(u8::read(reader)? == 1)),
            ParameterType::F32 => Ok(Parameter::F32(f32::read(reader)?)),
            ParameterType::Int => Ok(Parameter::Int(i32::read(reader)?)),
            ParameterType::Vec2 => Ok(Parameter::Vec2(types::Vec2::read(reader)?)),
            ParameterType::Vec3 => Ok(Parameter::Vec3(types::Vec3::read(reader)?)),
            ParameterType::Vec4 => Ok(Parameter::Vec4(types::Vec4::read(reader)?)),
            ParameterType::Color => Ok(Parameter::Color(types::Color::read(reader)?)),
            ParameterType::String32 => {
                let name = NullString::read(reader)?.to_string();
                add_parsed_string_to_table(&name);
                Ok(Parameter::String32(name))
            }
            ParameterType::String64 => {
                let name = NullString::read(reader)?.to_string();
                add_parsed_string_to_table(&name);
                Ok(Parameter::String64(name))
            }
            ParameterType::Curve1 => Ok(Parameter::Curve1(types::Curve1::read(reader)?)),
            ParameterType::Curve2 => Ok(Parameter::Curve2(types::Curve2::read(reader)?)),
            ParameterType::Curve3 => Ok(Parameter::Curve3(types::Curve3::read(reader)?)),
            ParameterType::Curve4 => Ok(Parameter::Curve4(types::Curve4::read(reader)?)),
            ParameterType::BufferInt => {
                reader.seek(SeekFrom::Current(-4))?;
                Ok(Parameter::BufferInt(types::BufferInt {
                    buffer: ParseBufferInt::read(reader)?.content,
                }))
            }
            ParameterType::BufferF32 => {
                reader.seek(SeekFrom::Current(-4))?;
                Ok(Parameter::BufferF32(types::BufferF32 {
                    buffer: ParseBufferF32::read(reader)?.content,
                }))
            }
            ParameterType::String256 => {
                let name = NullString::read(reader)?.to_string();
                add_parsed_string_to_table(&name);
                Ok(Parameter::String256(name))
            }
            ParameterType::Quat => Ok(Parameter::Quat(types::Quat::read(reader)?)),
            ParameterType::U32 => Ok(Parameter::U32(u32::read(reader)?)),
            ParameterType::BufferU32 => {
                reader.seek(SeekFrom::Current(-4))?;
                Ok(Parameter::BufferU32(types::BufferU32 {
                    buffer: ParseBufferU32::read(reader)?.content,
                }))
            }
            ParameterType::BufferBinary => {
                reader.seek(SeekFrom::Current(-4))?;
                Ok(Parameter::BufferBinary(types::BufferBinary {
                    buffer: ParseBufferBinary::read(reader)?.content,
                }))
            }
            ParameterType::StringRef => {
                let name = NullString::read(reader)?.to_string();
                add_parsed_string_to_table(&name);
                Ok(Parameter::StringRef(name))
            }
        }
    }
}