1use crate::error::{M2Error, Result};
2use crate::io_ext::{ReadExt, WriteExt};
3use std::io::{Read, Seek, Write};
4
5#[derive(Debug, Clone, Copy, Default, PartialEq)]
7pub struct M2Array<T> {
8 pub count: u32,
10 pub offset: u32,
12 _phantom: std::marker::PhantomData<T>,
14}
15
16impl<T> M2Array<T> {
17 pub fn new(count: u32, offset: u32) -> Self {
19 Self {
20 count,
21 offset,
22 _phantom: std::marker::PhantomData,
23 }
24 }
25
26 pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
28 let count = reader.read_u32_le()?;
29 let offset = reader.read_u32_le()?;
30
31 Ok(Self {
32 count,
33 offset,
34 _phantom: std::marker::PhantomData,
35 })
36 }
37
38 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
40 writer.write_u32_le(self.count)?;
41 writer.write_u32_le(self.offset)?;
42
43 Ok(())
44 }
45
46 pub fn is_empty(&self) -> bool {
48 self.count == 0
49 }
50
51 pub fn convert<U>(&self) -> M2Array<U> {
53 M2Array {
54 count: self.count,
55 offset: self.offset,
56 _phantom: std::marker::PhantomData,
57 }
58 }
59}
60
61pub fn read_array<T, R, F>(reader: &mut R, array: &M2Array<T>, parse_fn: F) -> Result<Vec<T>>
63where
64 R: Read + Seek,
65 F: Fn(&mut R) -> Result<T>,
66{
67 if array.is_empty() {
68 return Ok(Vec::new());
69 }
70
71 reader
73 .seek(std::io::SeekFrom::Start(array.offset as u64))
74 .map_err(M2Error::Io)?;
75
76 let mut result = Vec::with_capacity(array.count as usize);
78 for _ in 0..array.count {
79 result.push(parse_fn(reader)?);
80 }
81
82 Ok(result)
83}
84
85#[derive(Debug, Clone, Copy, PartialEq)]
87pub struct C3Vector {
88 pub x: f32,
89 pub y: f32,
90 pub z: f32,
91}
92
93impl C3Vector {
94 pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
96 let x = reader.read_f32_le()?;
97 let y = reader.read_f32_le()?;
98 let z = reader.read_f32_le()?;
99
100 Ok(Self { x, y, z })
101 }
102
103 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
105 writer.write_f32_le(self.x)?;
106 writer.write_f32_le(self.y)?;
107 writer.write_f32_le(self.z)?;
108
109 Ok(())
110 }
111
112 pub fn to_glam(&self) -> glam::Vec3 {
114 glam::Vec3::new(self.x, self.y, self.z)
115 }
116
117 pub fn from_glam(v: glam::Vec3) -> Self {
119 Self {
120 x: v.x,
121 y: v.y,
122 z: v.z,
123 }
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq)]
129pub struct C2Vector {
130 pub x: f32,
131 pub y: f32,
132}
133
134impl C2Vector {
135 pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
137 let x = reader.read_f32_le()?;
138 let y = reader.read_f32_le()?;
139
140 Ok(Self { x, y })
141 }
142
143 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
145 writer.write_f32_le(self.x)?;
146 writer.write_f32_le(self.y)?;
147
148 Ok(())
149 }
150
151 pub fn to_glam(&self) -> glam::Vec2 {
153 glam::Vec2::new(self.x, self.y)
154 }
155
156 pub fn from_glam(v: glam::Vec2) -> Self {
158 Self { x: v.x, y: v.y }
159 }
160}
161
162#[derive(Debug, Clone, PartialEq)]
164pub struct FixedString {
165 pub data: Vec<u8>,
166}
167
168impl FixedString {
169 pub fn parse<R: Read>(reader: &mut R, len: usize) -> Result<Self> {
171 let mut data = vec![0u8; len];
172 reader.read_exact(&mut data)?;
173
174 let null_pos = data.iter().position(|&b| b == 0).unwrap_or(len);
176 data.truncate(null_pos);
177
178 Ok(Self { data })
179 }
180
181 pub fn write<W: Write>(&self, writer: &mut W, len: usize) -> Result<()> {
183 let mut data = self.data.clone();
184 data.resize(len, 0);
185 writer.write_all(&data)?;
186
187 Ok(())
188 }
189
190 pub fn to_string_lossy(&self) -> String {
192 String::from_utf8_lossy(&self.data).to_string()
193 }
194}
195
196#[derive(Debug, Clone, Copy, PartialEq)]
198pub struct Quaternion {
199 pub x: f32,
200 pub y: f32,
201 pub z: f32,
202 pub w: f32,
203}
204
205impl Quaternion {
206 pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
208 let x = reader.read_f32_le()?;
209 let y = reader.read_f32_le()?;
210 let z = reader.read_f32_le()?;
211 let w = reader.read_f32_le()?;
212
213 Ok(Self { x, y, z, w })
214 }
215
216 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
218 writer.write_f32_le(self.x)?;
219 writer.write_f32_le(self.y)?;
220 writer.write_f32_le(self.z)?;
221 writer.write_f32_le(self.w)?;
222
223 Ok(())
224 }
225
226 pub fn to_glam(&self) -> glam::Quat {
228 glam::Quat::from_xyzw(self.x, self.y, self.z, self.w)
229 }
230
231 pub fn from_glam(q: glam::Quat) -> Self {
233 Self {
234 x: q.x,
235 y: q.y,
236 z: q.z,
237 w: q.w,
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use std::io::Cursor;
246
247 #[test]
248 fn test_m2array_parse() {
249 let data = [
250 0x05, 0x00, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, ];
253
254 let mut cursor = Cursor::new(data);
255 let array = M2Array::<u32>::parse(&mut cursor).unwrap();
256
257 assert_eq!(array.count, 5);
258 assert_eq!(array.offset, 0x3020);
259 }
260
261 #[test]
262 fn test_m2array_write() {
263 let array = M2Array::<u32>::new(5, 0x3020);
264 let mut cursor = Cursor::new(Vec::new());
265
266 array.write(&mut cursor).unwrap();
267
268 let data = cursor.into_inner();
269 assert_eq!(
270 data,
271 [
272 0x05, 0x00, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, ]
275 );
276 }
277
278 #[test]
279 fn test_c3vector_parse() {
280 let data = [
281 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, ];
285
286 let mut cursor = Cursor::new(data);
287 let vector = C3Vector::parse(&mut cursor).unwrap();
288
289 assert_eq!(vector.x, 1.0);
290 assert_eq!(vector.y, 2.0);
291 assert_eq!(vector.z, 3.0);
292 }
293
294 #[test]
295 fn test_c2vector_parse() {
296 let data = [
297 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, ];
300
301 let mut cursor = Cursor::new(data);
302 let vector = C2Vector::parse(&mut cursor).unwrap();
303
304 assert_eq!(vector.x, 1.0);
305 assert_eq!(vector.y, 2.0);
306 }
307
308 #[test]
309 fn test_fixed_string_parse() {
310 let data = [b'T', b'e', b's', b't', 0, 0, 0, 0];
311
312 let mut cursor = Cursor::new(data);
313 let string = FixedString::parse(&mut cursor, 8).unwrap();
314
315 assert_eq!(string.data, b"Test");
316 assert_eq!(string.to_string_lossy(), "Test");
317 }
318}