1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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()
    }
}