1use crate::error::{M2Error, Result};
2use crate::io_ext::{ReadExt, WriteExt};
3use std::io::{Read, Seek, SeekFrom, 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, Default)]
164pub struct FixedString {
165 pub data: Vec<u8>,
166}
167
168impl FixedString {
169 pub fn len(&self) -> usize {
170 self.data.len()
171 }
172
173 pub fn is_empty(&self) -> bool {
175 self.data.is_empty()
176 }
177
178 pub fn parse<R: Read + Seek>(reader: &mut R, len: usize) -> Result<Self> {
180 let mut data = vec![0u8; len];
181 reader.read_exact(&mut data)?;
182
183 let null_pos = data.iter().position(|&b| b == 0).unwrap_or(len);
185 data.truncate(null_pos);
186
187 Ok(Self { data })
188 }
189
190 pub fn write<W: Write>(&self, writer: &mut W, len: usize) -> Result<()> {
192 let mut data = self.data.clone();
193 data.resize(len, 0);
194 writer.write_all(&data)?;
195
196 Ok(())
197 }
198
199 pub fn to_string_lossy(&self) -> String {
201 String::from_utf8_lossy(&self.data).to_string()
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Default)]
206pub struct M2ArrayString {
207 pub string: FixedString,
208 pub array: M2Array<u8>,
209}
210
211impl M2ArrayString {
212 pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
213 let array = M2Array::<u8>::parse(reader)?;
214 let current_pos = reader.stream_position()?;
215 reader.seek(SeekFrom::Start(array.offset as u64))?;
216 let string = FixedString::parse(reader, array.count as usize)?;
217 reader.seek(SeekFrom::Start(current_pos))?;
218 Ok(Self { string, array })
219 }
220
221 pub fn is_empty(&self) -> bool {
222 self.array.is_empty()
223 }
224
225 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
227 writer.write_u32_le(self.array.count)?;
228 writer.write_u32_le(self.array.offset)?;
229
230 Ok(())
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq)]
236pub struct Quaternion {
237 pub x: f32,
238 pub y: f32,
239 pub z: f32,
240 pub w: f32,
241}
242
243impl Quaternion {
244 pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
246 let x = reader.read_f32_le()?;
247 let y = reader.read_f32_le()?;
248 let z = reader.read_f32_le()?;
249 let w = reader.read_f32_le()?;
250
251 Ok(Self { x, y, z, w })
252 }
253
254 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
256 writer.write_f32_le(self.x)?;
257 writer.write_f32_le(self.y)?;
258 writer.write_f32_le(self.z)?;
259 writer.write_f32_le(self.w)?;
260
261 Ok(())
262 }
263
264 pub fn to_glam(&self) -> glam::Quat {
266 glam::Quat::from_xyzw(self.x, self.y, self.z, self.w)
267 }
268
269 pub fn from_glam(q: glam::Quat) -> Self {
271 Self {
272 x: q.x,
273 y: q.y,
274 z: q.z,
275 w: q.w,
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use std::io::Cursor;
284
285 #[test]
286 fn test_m2array_parse() {
287 let data = [
288 0x05, 0x00, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, ];
291
292 let mut cursor = Cursor::new(data);
293 let array = M2Array::<u32>::parse(&mut cursor).unwrap();
294
295 assert_eq!(array.count, 5);
296 assert_eq!(array.offset, 0x3020);
297 }
298
299 #[test]
300 fn test_m2array_write() {
301 let array = M2Array::<u32>::new(5, 0x3020);
302 let mut cursor = Cursor::new(Vec::new());
303
304 array.write(&mut cursor).unwrap();
305
306 let data = cursor.into_inner();
307 assert_eq!(
308 data,
309 [
310 0x05, 0x00, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, ]
313 );
314 }
315
316 #[test]
317 fn test_c3vector_parse() {
318 let data = [
319 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, ];
323
324 let mut cursor = Cursor::new(data);
325 let vector = C3Vector::parse(&mut cursor).unwrap();
326
327 assert_eq!(vector.x, 1.0);
328 assert_eq!(vector.y, 2.0);
329 assert_eq!(vector.z, 3.0);
330 }
331
332 #[test]
333 fn test_c2vector_parse() {
334 let data = [
335 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, ];
338
339 let mut cursor = Cursor::new(data);
340 let vector = C2Vector::parse(&mut cursor).unwrap();
341
342 assert_eq!(vector.x, 1.0);
343 assert_eq!(vector.y, 2.0);
344 }
345
346 #[test]
347 fn test_fixed_string_parse() {
348 let data = [b'T', b'e', b's', b't', 0, 0, 0, 0];
349
350 let mut cursor = Cursor::new(data);
351 let string = FixedString::parse(&mut cursor, 8).unwrap();
352
353 assert_eq!(string.data, b"Test");
354 assert_eq!(string.to_string_lossy(), "Test");
355 }
356}