motion_list_rs 1.5.0

Library for working with motion_list.bin files in Smash Ultimate
Documentation
use diff::Diff;
use hash40::*;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;

pub const MAGIC: Hash40 = hash40("motion");

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MList {
    pub motion_path: Hash40,
    pub list: IndexMap<Hash40, Motion>,
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Diff)]
#[diff(attr(#[derive(Debug, Serialize, Deserialize)]))]
pub struct Motion {
    pub game_script: Hash40,
    pub flags: Flags,
    pub blend_frames: u8,
    pub animations: Vec<Animation>,
    pub scripts: Vec<Hash40>,
    pub extra: Option<Extra>,
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Diff)]
#[diff(attr(#[derive(Debug, PartialEq, Serialize, Deserialize)]))]
pub struct Animation {
    pub name: Hash40,
    pub unk: u8,
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Diff)]
#[diff(attr(#[derive(Debug, PartialEq, Serialize, Deserialize)]))]
pub struct Extra {
    pub xlu_start: u8,
    pub xlu_end: u8,
    pub cancel_frame: u8,
    pub no_stop_intp: bool,
}

macro_rules! make_flags {
    ($first:ident, $($names:ident),*) => {
        #[derive(Debug, Default, PartialEq, Copy, Clone, Serialize, Deserialize, Diff)]
        #[diff(attr(#[derive(Debug, Serialize, Deserialize)]))]
        pub struct Flags {
            pub $first: bool,
            $(pub $names: bool),*
        }

        impl From<u16> for Flags {
            fn from(f: u16) -> Flags {
                let mut mask = 1;
                let $first = (f & mask) != 0;
                $(
                    mask <<= 1;
                    let $names = (f & mask) != 0;
                )*
                Flags { $first, $($names),* }
            }
        }

        impl From<Flags> for u16 {
            fn from(f: Flags) -> u16 {
                let mut mask = 1;
                let mut ret = f.$first as u16;
                $(
                    mask <<= 1;
                    if f.$names { ret |= mask }
                )*
                ret
            }
        }
    };
}

make_flags!(
    turn, r#loop, r#move, fix_trans, fix_rot, fix_scale, unk_40, unk_80, unk_100, unk_200, unk_400,
    unk_800, unk_1000, unk_2000
);

#[derive(Debug, Serialize, Deserialize)]
pub struct MListDiff {
    motion_path: <Hash40 as Diff>::Repr,
    altered: IndexMap<Hash40, <Motion as Diff>::Repr>,
    removed: HashSet<Hash40>,
}

impl Diff for MList {
    type Repr = MListDiff;

    fn diff(&self, other: &Self) -> Self::Repr {
        let mut diff = MListDiff {
            motion_path: None,
            altered: IndexMap::new(),
            removed: HashSet::new(),
        };
        if self.motion_path != other.motion_path {
            diff.motion_path = Some(other.motion_path);
        }
        for (key, value) in &self.list {
            if let Some(other_value) = other.list.get(key) {
                if value != other_value {
                    diff.altered.insert(key.clone(), value.diff(other_value));
                }
            } else {
                diff.removed.insert(key.clone());
            }
        }
        for (key, value) in &other.list {
            if let None = self.list.get(key) {
                diff.altered
                    .insert(key.clone(), Motion::identity().diff(&value));
            }
        }
        diff
    }

    fn apply(&mut self, diff: &Self::Repr) {
        if let Some(path_diff) = diff.motion_path {
            self.motion_path = path_diff;
        }
        diff.removed.iter().for_each(|del| {
            self.list.swap_remove(del);
        });
        for (key, change) in &diff.altered {
            if let Some(original) = self.list.get_mut(key) {
                original.apply(change);
            } else {
                self.list
                    .insert(key.clone(), Motion::identity().apply_new(change));
            }
        }
    }

    fn identity() -> Self {
        Default::default()
    }
}