1use std::collections::HashMap;
4use std::io::{self, Read, Seek, Write};
5
6use crate::error::{Result, WdlError};
7use crate::version::WdlVersion;
8
9pub const MVER_MAGIC: [u8; 4] = [b'R', b'E', b'V', b'M'];
11pub const MWMO_MAGIC: [u8; 4] = [b'O', b'M', b'W', b'M'];
13pub const MWID_MAGIC: [u8; 4] = [b'D', b'I', b'W', b'M'];
15pub const MODF_MAGIC: [u8; 4] = [b'F', b'D', b'O', b'M'];
17pub const MAOF_MAGIC: [u8; 4] = [b'F', b'O', b'A', b'M'];
19pub const MARE_MAGIC: [u8; 4] = [b'E', b'R', b'A', b'M'];
21pub const MAHO_MAGIC: [u8; 4] = [b'O', b'H', b'A', b'M'];
23pub const MLDD_MAGIC: [u8; 4] = [b'D', b'D', b'L', b'M'];
25pub const MLDX_MAGIC: [u8; 4] = [b'X', b'D', b'L', b'M'];
27pub const MLMD_MAGIC: [u8; 4] = [b'D', b'M', b'L', b'M'];
29pub const MLMX_MAGIC: [u8; 4] = [b'X', b'M', b'L', b'M'];
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct Vec3d {
35 pub x: f32,
37 pub y: f32,
39 pub z: f32,
41}
42
43impl Vec3d {
44 pub fn new(x: f32, y: f32, z: f32) -> Self {
46 Self { x, y, z }
47 }
48
49 pub fn origin() -> Self {
51 Self::new(0.0, 0.0, 0.0)
52 }
53
54 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
56 let mut buf = [0u8; 4];
57 reader.read_exact(&mut buf)?;
58 let x = f32::from_le_bytes(buf);
59 reader.read_exact(&mut buf)?;
60 let y = f32::from_le_bytes(buf);
61 reader.read_exact(&mut buf)?;
62 let z = f32::from_le_bytes(buf);
63 Ok(Self::new(x, y, z))
64 }
65
66 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
68 writer.write_all(&self.x.to_le_bytes())?;
69 writer.write_all(&self.y.to_le_bytes())?;
70 writer.write_all(&self.z.to_le_bytes())?;
71 Ok(())
72 }
73}
74
75#[derive(Debug, Clone, PartialEq)]
77pub struct BoundingBox {
78 pub min: Vec3d,
80 pub max: Vec3d,
82}
83
84impl BoundingBox {
85 pub fn new(min: Vec3d, max: Vec3d) -> Self {
87 Self { min, max }
88 }
89
90 pub fn zero() -> Self {
92 Self::new(Vec3d::origin(), Vec3d::origin())
93 }
94
95 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
97 let min = Vec3d::read(reader)?;
98 let max = Vec3d::read(reader)?;
99 Ok(Self::new(min, max))
100 }
101
102 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
104 self.min.write(writer)?;
105 self.max.write(writer)?;
106 Ok(())
107 }
108}
109
110#[derive(Debug, Clone)]
112pub struct Chunk {
113 pub magic: [u8; 4],
115 pub size: u32,
117 pub data: Vec<u8>,
119}
120
121impl Chunk {
122 pub fn new(magic: [u8; 4], data: Vec<u8>) -> Self {
124 let size = data.len() as u32;
125 Self { magic, size, data }
126 }
127
128 pub fn read<R: Read + Seek>(reader: &mut R) -> io::Result<Self> {
130 let mut magic = [0u8; 4];
131 if reader.read_exact(&mut magic).is_err() {
132 return Err(io::Error::new(
134 io::ErrorKind::UnexpectedEof,
135 "unexpected end of file",
136 ));
137 }
138
139 let mut size_buf = [0u8; 4];
140 reader.read_exact(&mut size_buf)?;
141 let size = u32::from_le_bytes(size_buf);
142 let mut data = vec![0u8; size as usize];
143 reader.read_exact(&mut data)?;
144
145 Ok(Self { magic, size, data })
146 }
147
148 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
150 writer.write_all(&self.magic)?;
151 writer.write_all(&self.size.to_le_bytes())?;
152 writer.write_all(&self.data)?;
153 Ok(())
154 }
155
156 pub fn magic_str(&self) -> String {
158 String::from_utf8_lossy(&self.magic).to_string()
159 }
160}
161
162#[derive(Debug, Clone)]
164pub struct ModelPlacement {
165 pub id: u32,
167 pub wmo_id: u32,
169 pub position: Vec3d,
171 pub rotation: Vec3d,
173 pub bounds: BoundingBox,
175 pub flags: u16,
177 pub doodad_set: u16,
179 pub name_set: u16,
181 pub padding: u16,
183}
184
185impl ModelPlacement {
186 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
188 let mut buf4 = [0u8; 4];
189 let mut buf2 = [0u8; 2];
190
191 reader.read_exact(&mut buf4)?;
192 let wmo_id = u32::from_le_bytes(buf4);
193 reader.read_exact(&mut buf4)?;
194 let id = u32::from_le_bytes(buf4);
195 let position = Vec3d::read(reader)?;
196 let rotation = Vec3d::read(reader)?;
197 let bounds = BoundingBox::read(reader)?;
198 reader.read_exact(&mut buf2)?;
199 let flags = u16::from_le_bytes(buf2);
200 reader.read_exact(&mut buf2)?;
201 let doodad_set = u16::from_le_bytes(buf2);
202 reader.read_exact(&mut buf2)?;
203 let name_set = u16::from_le_bytes(buf2);
204
205 reader.read_exact(&mut buf2)?;
206 let padding = u16::from_le_bytes(buf2);
207
208 Ok(Self {
209 id,
210 wmo_id,
211 position,
212 rotation,
213 bounds,
214 flags,
215 doodad_set,
216 name_set,
217 padding,
218 })
219 }
220
221 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
223 writer.write_all(&self.wmo_id.to_le_bytes())?;
224 writer.write_all(&self.id.to_le_bytes())?;
225 self.position.write(writer)?;
226 self.rotation.write(writer)?;
227 self.bounds.write(writer)?;
228 writer.write_all(&self.flags.to_le_bytes())?;
229 writer.write_all(&self.doodad_set.to_le_bytes())?;
230 writer.write_all(&self.name_set.to_le_bytes())?;
231
232 writer.write_all(&self.padding.to_le_bytes())?;
233
234 Ok(())
235 }
236}
237
238#[derive(Debug, Clone)]
240pub struct M2Placement {
241 pub id: u32,
243 pub m2_id: u32,
245 pub position: Vec3d,
247 pub rotation: Vec3d,
249 pub scale: f32,
251 pub flags: u32,
253}
254
255impl M2Placement {
256 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
258 let mut buf = [0u8; 4];
259
260 reader.read_exact(&mut buf)?;
261 let id = u32::from_le_bytes(buf);
262 reader.read_exact(&mut buf)?;
263 let m2_id = u32::from_le_bytes(buf);
264 let position = Vec3d::read(reader)?;
265 let rotation = Vec3d::read(reader)?;
266 reader.read_exact(&mut buf)?;
267 let scale = f32::from_le_bytes(buf);
268 reader.read_exact(&mut buf)?;
269 let flags = u32::from_le_bytes(buf);
270
271 Ok(Self {
272 id,
273 m2_id,
274 position,
275 rotation,
276 scale,
277 flags,
278 })
279 }
280
281 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
283 writer.write_all(&self.id.to_le_bytes())?;
284 writer.write_all(&self.m2_id.to_le_bytes())?;
285 self.position.write(writer)?;
286 self.rotation.write(writer)?;
287 writer.write_all(&self.scale.to_le_bytes())?;
288 writer.write_all(&self.flags.to_le_bytes())?;
289
290 Ok(())
291 }
292}
293
294#[derive(Debug, Clone)]
296pub struct M2VisibilityInfo {
297 pub bounds: BoundingBox,
299 pub radius: f32,
301}
302
303impl M2VisibilityInfo {
304 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
306 let bounds = BoundingBox::read(reader)?;
307 let mut buf = [0u8; 4];
308 reader.read_exact(&mut buf)?;
309 let radius = f32::from_le_bytes(buf);
310
311 Ok(Self { bounds, radius })
312 }
313
314 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
316 self.bounds.write(writer)?;
317 writer.write_all(&self.radius.to_le_bytes())?;
318
319 Ok(())
320 }
321}
322
323#[derive(Debug, Clone)]
332pub struct HeightMapTile {
333 pub outer_values: Vec<i16>,
336 pub inner_values: Vec<i16>,
339}
340
341impl Default for HeightMapTile {
342 fn default() -> Self {
343 Self::new()
344 }
345}
346
347impl HeightMapTile {
348 pub const OUTER_COUNT: usize = 17 * 17;
350 pub const INNER_COUNT: usize = 16 * 16;
352 pub const TOTAL_COUNT: usize = Self::OUTER_COUNT + Self::INNER_COUNT;
354
355 pub fn new() -> Self {
357 Self {
358 outer_values: vec![0; Self::OUTER_COUNT],
359 inner_values: vec![0; Self::INNER_COUNT],
360 }
361 }
362
363 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
365 let mut outer_values = Vec::with_capacity(Self::OUTER_COUNT);
366 let mut buf = [0u8; 2];
367 for _ in 0..Self::OUTER_COUNT {
368 reader.read_exact(&mut buf)?;
369 outer_values.push(i16::from_le_bytes(buf));
370 }
371
372 let mut inner_values = Vec::with_capacity(Self::INNER_COUNT);
373 for _ in 0..Self::INNER_COUNT {
374 reader.read_exact(&mut buf)?;
375 inner_values.push(i16::from_le_bytes(buf));
376 }
377
378 Ok(Self {
379 outer_values,
380 inner_values,
381 })
382 }
383
384 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
386 for value in &self.outer_values {
387 writer.write_all(&value.to_le_bytes())?;
388 }
389
390 for value in &self.inner_values {
391 writer.write_all(&value.to_le_bytes())?;
392 }
393
394 Ok(())
395 }
396}
397
398#[derive(Debug, Clone)]
405pub struct HolesData {
406 pub hole_masks: [u16; 16],
409}
410
411impl Default for HolesData {
412 fn default() -> Self {
413 Self::new()
414 }
415}
416
417impl HolesData {
418 pub const MASK_COUNT: usize = 16;
420
421 pub fn new() -> Self {
423 Self {
424 hole_masks: [0xFFFF; Self::MASK_COUNT],
425 }
426 }
427
428 pub fn all_holes() -> Self {
430 Self {
431 hole_masks: [0; Self::MASK_COUNT],
432 }
433 }
434
435 pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
437 let mut hole_masks = [0u16; Self::MASK_COUNT];
438 let mut buf = [0u8; 2];
439 for mask in &mut hole_masks {
440 reader.read_exact(&mut buf)?;
441 *mask = u16::from_le_bytes(buf);
442 }
443
444 Ok(Self { hole_masks })
445 }
446
447 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
449 for mask in &self.hole_masks {
450 writer.write_all(&mask.to_le_bytes())?;
451 }
452
453 Ok(())
454 }
455
456 pub fn has_hole(&self, x: usize, y: usize) -> bool {
467 if x >= 16 || y >= 16 {
468 return false;
469 }
470
471 let mask = self.hole_masks[y];
472 (mask & (1 << x)) == 0
473 }
474
475 pub fn set_hole(&mut self, x: usize, y: usize, has_hole: bool) {
483 if x >= 16 || y >= 16 {
484 return;
485 }
486
487 if has_hole {
488 self.hole_masks[y] &= !(1 << x);
490 } else {
491 self.hole_masks[y] |= 1 << x;
493 }
494 }
495}
496
497#[derive(Debug)]
499pub struct WdlFile {
500 pub version: WdlVersion,
502 pub version_number: u32,
504 pub map_tile_offsets: [u32; 64 * 64],
508 pub heightmap_tiles: HashMap<(u32, u32), HeightMapTile>,
510 pub holes_data: HashMap<(u32, u32), HolesData>,
512 pub wmo_filenames: Vec<String>,
514 pub wmo_indices: Vec<u32>,
516 pub wmo_placements: Vec<ModelPlacement>,
518 pub m2_placements: Vec<M2Placement>,
520 pub m2_visibility: Vec<M2VisibilityInfo>,
522 pub wmo_legion_placements: Vec<M2Placement>,
524 pub wmo_legion_visibility: Vec<M2VisibilityInfo>,
526 pub chunks: Vec<Chunk>,
528}
529
530impl WdlFile {
531 pub fn new() -> Self {
533 Self {
534 version: WdlVersion::default(),
535 version_number: WdlVersion::default().version_number(),
536 map_tile_offsets: [0; 64 * 64],
537 heightmap_tiles: HashMap::new(),
538 holes_data: HashMap::new(),
539 wmo_filenames: Vec::new(),
540 wmo_indices: Vec::new(),
541 wmo_placements: Vec::new(),
542 m2_placements: Vec::new(),
543 m2_visibility: Vec::new(),
544 wmo_legion_placements: Vec::new(),
545 wmo_legion_visibility: Vec::new(),
546 chunks: Vec::new(),
547 }
548 }
549
550 pub fn with_version(version: WdlVersion) -> Self {
552 let mut file = Self::new();
553 file.version = version;
554 file.version_number = version.version_number();
555 file
556 }
557
558 pub fn validate(&self) -> Result<()> {
560 if self.version_number != self.version.version_number() {
562 return Err(WdlError::ValidationError(format!(
563 "Version number mismatch: file has {}, expected {}",
564 self.version_number,
565 self.version.version_number()
566 )));
567 }
568
569 Ok(())
575 }
576
577 pub fn convert_to(&self, target_version: WdlVersion) -> Result<Self> {
579 let mut new_file = WdlFile::with_version(target_version);
581
582 new_file.map_tile_offsets = self.map_tile_offsets;
584 new_file.heightmap_tiles = self.heightmap_tiles.clone();
585 new_file.holes_data = self.holes_data.clone();
586
587 if target_version.has_wmo_chunks() {
589 new_file.wmo_filenames = self.wmo_filenames.clone();
590 new_file.wmo_indices = self.wmo_indices.clone();
591 new_file.wmo_placements = self.wmo_placements.clone();
592 }
593
594 if target_version.has_ml_chunks() {
595 new_file.m2_placements = self.m2_placements.clone();
596 new_file.m2_visibility = self.m2_visibility.clone();
597 new_file.wmo_legion_placements = self.wmo_legion_placements.clone();
598 new_file.wmo_legion_visibility = self.wmo_legion_visibility.clone();
599
600 if !self.version.has_ml_chunks() && self.version.has_wmo_chunks() {
603 }
605 }
606
607 Ok(new_file)
608 }
609}
610
611impl Default for WdlFile {
612 fn default() -> Self {
613 Self::new()
614 }
615}
616
617#[cfg(test)]
618mod tests {
619 use super::*;
620
621 #[test]
622 fn test_vec3d() {
623 let vec = Vec3d::new(1.0, 2.0, 3.0);
624 assert_eq!(vec.x, 1.0);
625 assert_eq!(vec.y, 2.0);
626 assert_eq!(vec.z, 3.0);
627
628 let origin = Vec3d::origin();
629 assert_eq!(origin.x, 0.0);
630 assert_eq!(origin.y, 0.0);
631 assert_eq!(origin.z, 0.0);
632 }
633
634 #[test]
635 fn test_bounding_box() {
636 let min = Vec3d::new(1.0, 2.0, 3.0);
637 let max = Vec3d::new(4.0, 5.0, 6.0);
638 let bbox = BoundingBox::new(min.clone(), max.clone());
639
640 assert_eq!(bbox.min, min);
641 assert_eq!(bbox.max, max);
642
643 let zero = BoundingBox::zero();
644 assert_eq!(zero.min, Vec3d::origin());
645 assert_eq!(zero.max, Vec3d::origin());
646 }
647
648 #[test]
649 fn test_holes_data() {
650 let mut holes = HolesData::new();
651
652 for x in 0..16 {
654 for y in 0..16 {
655 assert!(!holes.has_hole(x, y));
656 }
657 }
658
659 holes.set_hole(5, 7, true);
661 assert!(holes.has_hole(5, 7));
662
663 holes.set_hole(5, 7, false);
665 assert!(!holes.has_hole(5, 7));
666
667 let all_holes = HolesData::all_holes();
669 for x in 0..16 {
670 for y in 0..16 {
671 assert!(all_holes.has_hole(x, y));
672 }
673 }
674 }
675}