1use crate::io_ext::{ReadExt, WriteExt};
2use std::io::{Read, Write};
3
4use crate::common::M2Array;
5use crate::error::Result;
6use crate::version::M2Version;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum M2EventType {
11 Sound = 0,
13 SoundStop = 1,
15 SpellCastOMG = 2,
17 Hide = 3,
19 Unknown4 = 4,
21 StandOrLand = 5,
23 SpeechEmote = 6,
25 FootstepFront = 7,
27 FootstepBack = 8,
29 PlaySoundKitFromTable = 9,
31}
32
33impl M2EventType {
34 pub fn from_u32(value: u32) -> Option<Self> {
36 match value {
37 0 => Some(Self::Sound),
38 1 => Some(Self::SoundStop),
39 2 => Some(Self::SpellCastOMG),
40 3 => Some(Self::Hide),
41 4 => Some(Self::Unknown4),
42 5 => Some(Self::StandOrLand),
43 6 => Some(Self::SpeechEmote),
44 7 => Some(Self::FootstepFront),
45 8 => Some(Self::FootstepBack),
46 9 => Some(Self::PlaySoundKitFromTable),
47 _ => None,
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
65pub struct M2Event {
66 pub identifier: [u8; 4],
68 pub data: u32,
70 pub bone_index: i16,
72 pub unknown: u16,
74 pub position: [f32; 3],
76 pub interp_type: u16,
78 pub global_sequence: i16,
80 pub ranges: M2Array<u32>,
82 pub times: M2Array<u32>,
84}
85
86impl M2Event {
87 pub fn parse<R: Read>(reader: &mut R, _version: u32) -> Result<Self> {
91 let mut identifier = [0u8; 4];
92 reader.read_exact(&mut identifier)?;
93
94 let data = reader.read_u32_le()?;
95 let bone_index = reader.read_i16_le()?;
96 let unknown = reader.read_u16_le()?;
97
98 let mut position = [0.0; 3];
99 for item in &mut position {
100 *item = reader.read_f32_le()?;
101 }
102
103 let interp_type = reader.read_u16_le()?;
104 let global_sequence = reader.read_i16_le()?;
105
106 let ranges = M2Array::parse(reader)?;
107 let times = M2Array::parse(reader)?;
108
109 Ok(Self {
110 identifier,
111 data,
112 bone_index,
113 unknown,
114 position,
115 interp_type,
116 global_sequence,
117 ranges,
118 times,
119 })
120 }
121
122 pub fn write<W: Write>(&self, writer: &mut W, _version: u32) -> Result<()> {
124 writer.write_all(&self.identifier)?;
125 writer.write_u32_le(self.data)?;
126 writer.write_i16_le(self.bone_index)?;
127 writer.write_u16_le(self.unknown)?;
128
129 for &pos in &self.position {
130 writer.write_f32_le(pos)?;
131 }
132
133 writer.write_u16_le(self.interp_type)?;
134 writer.write_i16_le(self.global_sequence)?;
135
136 self.ranges.write(writer)?;
137 self.times.write(writer)?;
138
139 Ok(())
140 }
141
142 pub fn convert(&self, _target_version: M2Version) -> Self {
144 self.clone()
145 }
146
147 pub fn new(identifier: [u8; 4], bone_index: i16) -> Self {
149 Self {
150 identifier,
151 data: 0,
152 bone_index,
153 unknown: 0,
154 position: [0.0, 0.0, 0.0],
155 interp_type: 0,
156 global_sequence: -1,
157 ranges: M2Array::new(0, 0),
158 times: M2Array::new(0, 0),
159 }
160 }
161
162 pub fn identifier_str(&self) -> String {
164 String::from_utf8_lossy(&self.identifier).to_string()
165 }
166
167 pub const fn size() -> usize {
169 44
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use std::io::Cursor;
177
178 #[test]
179 fn test_event_parse_write() {
180 let event = M2Event::new(*b"$CST", 25);
181
182 let mut data = Vec::new();
184 event
185 .write(&mut data, M2Version::Vanilla.to_header_version())
186 .unwrap();
187
188 assert_eq!(data.len(), 44);
190
191 let mut cursor = Cursor::new(data);
193 let parsed = M2Event::parse(&mut cursor, M2Version::Vanilla.to_header_version()).unwrap();
194
195 assert_eq!(parsed.identifier, *b"$CST");
196 assert_eq!(parsed.bone_index, 25);
197 assert_eq!(parsed.global_sequence, -1);
198 assert_eq!(parsed.identifier_str(), "$CST");
199 }
200
201 #[test]
202 fn test_event_size() {
203 assert_eq!(M2Event::size(), 44);
204 }
205
206 #[test]
207 fn test_event_types() {
208 assert_eq!(M2EventType::from_u32(0), Some(M2EventType::Sound));
209 assert_eq!(M2EventType::from_u32(5), Some(M2EventType::StandOrLand));
210 assert_eq!(
211 M2EventType::from_u32(9),
212 Some(M2EventType::PlaySoundKitFromTable)
213 );
214 assert_eq!(M2EventType::from_u32(20), None);
215 }
216}