use std::fs::File;
use std::io::{BufWriter, Cursor, Write};
use std::path::Path;
use crate::api::RootAdt;
use crate::chunks::mh2o::Mh2oChunk;
use crate::chunks::{
DoodadPlacement, MampChunk, MbbbChunk, MbmhChunk, MbmiChunk, MbnvChunk, McnkChunk, MfboChunk,
MtxfChunk, MtxpChunk, WmoPlacement,
};
use crate::error::Result;
use crate::version::AdtVersion;
use super::serializer;
#[derive(Debug, Clone)]
pub struct BuiltAdt {
version: AdtVersion,
textures: Vec<String>,
models: Vec<String>,
wmos: Vec<String>,
doodad_placements: Vec<DoodadPlacement>,
wmo_placements: Vec<WmoPlacement>,
mcnk_chunks: Vec<McnkChunk>,
flight_bounds: Option<MfboChunk>,
water_data: Option<Mh2oChunk>,
texture_flags: Option<MtxfChunk>,
texture_amplifier: Option<MampChunk>,
texture_params: Option<MtxpChunk>,
blend_mesh_headers: Option<MbmhChunk>,
blend_mesh_bounds: Option<MbbbChunk>,
blend_mesh_vertices: Option<MbnvChunk>,
blend_mesh_indices: Option<MbmiChunk>,
}
impl BuiltAdt {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
version: AdtVersion,
textures: Vec<String>,
models: Vec<String>,
wmos: Vec<String>,
doodad_placements: Vec<DoodadPlacement>,
wmo_placements: Vec<WmoPlacement>,
mcnk_chunks: Vec<McnkChunk>,
flight_bounds: Option<MfboChunk>,
water_data: Option<Mh2oChunk>,
texture_flags: Option<MtxfChunk>,
texture_amplifier: Option<MampChunk>,
texture_params: Option<MtxpChunk>,
blend_mesh_headers: Option<MbmhChunk>,
blend_mesh_bounds: Option<MbbbChunk>,
blend_mesh_vertices: Option<MbnvChunk>,
blend_mesh_indices: Option<MbmiChunk>,
) -> Self {
Self {
version,
textures,
models,
wmos,
doodad_placements,
wmo_placements,
mcnk_chunks,
flight_bounds,
water_data,
texture_flags,
texture_amplifier,
texture_params,
blend_mesh_headers,
blend_mesh_bounds,
blend_mesh_vertices,
blend_mesh_indices,
}
}
#[must_use]
pub fn from_root_adt(root: RootAdt, target_version: Option<AdtVersion>) -> Self {
let version = target_version.unwrap_or(root.version);
let flight_bounds = if version >= AdtVersion::TBC {
root.flight_bounds.or(Some(MfboChunk {
max_plane: [0; 9],
min_plane: [0; 9],
}))
} else {
None };
let water_data = if version >= AdtVersion::WotLK {
root.water_data
} else {
None };
let texture_flags = if version >= AdtVersion::WotLK {
root.texture_flags
} else {
None };
let texture_amplifier = if version >= AdtVersion::Cataclysm {
root.texture_amplifier
} else {
None };
let texture_params = if version >= AdtVersion::MoP {
root.texture_params
} else {
None };
let blend_mesh_headers = if version >= AdtVersion::MoP {
root.blend_mesh_headers
} else {
None
};
let blend_mesh_bounds = if version >= AdtVersion::MoP {
root.blend_mesh_bounds
} else {
None
};
let blend_mesh_vertices = if version >= AdtVersion::MoP {
root.blend_mesh_vertices
} else {
None
};
let blend_mesh_indices = if version >= AdtVersion::MoP {
root.blend_mesh_indices
} else {
None
};
Self {
version,
textures: root.textures,
models: root.models,
wmos: root.wmos,
doodad_placements: root.doodad_placements,
wmo_placements: root.wmo_placements,
mcnk_chunks: root.mcnk_chunks,
flight_bounds,
water_data,
texture_flags,
texture_amplifier,
texture_params,
blend_mesh_headers,
blend_mesh_bounds,
blend_mesh_vertices,
blend_mesh_indices,
}
}
#[must_use]
pub fn version(&self) -> AdtVersion {
self.version
}
#[must_use]
pub fn textures(&self) -> &[String] {
&self.textures
}
#[must_use]
pub fn models(&self) -> &[String] {
&self.models
}
#[must_use]
pub fn wmos(&self) -> &[String] {
&self.wmos
}
#[must_use]
pub fn doodad_placements(&self) -> &[DoodadPlacement] {
&self.doodad_placements
}
#[must_use]
pub fn wmo_placements(&self) -> &[WmoPlacement] {
&self.wmo_placements
}
#[must_use]
pub fn mcnk_chunks(&self) -> &[McnkChunk] {
&self.mcnk_chunks
}
#[must_use]
pub fn flight_bounds(&self) -> Option<&MfboChunk> {
self.flight_bounds.as_ref()
}
#[must_use]
pub fn water_data(&self) -> Option<&Mh2oChunk> {
self.water_data.as_ref()
}
#[must_use]
pub fn texture_flags(&self) -> Option<&MtxfChunk> {
self.texture_flags.as_ref()
}
#[must_use]
pub fn texture_amplifier(&self) -> Option<&MampChunk> {
self.texture_amplifier.as_ref()
}
#[must_use]
pub fn texture_params(&self) -> Option<&MtxpChunk> {
self.texture_params.as_ref()
}
#[must_use]
pub fn blend_mesh_headers(&self) -> Option<&MbmhChunk> {
self.blend_mesh_headers.as_ref()
}
#[must_use]
pub fn blend_mesh_bounds(&self) -> Option<&MbbbChunk> {
self.blend_mesh_bounds.as_ref()
}
#[must_use]
pub fn blend_mesh_vertices(&self) -> Option<&MbnvChunk> {
self.blend_mesh_vertices.as_ref()
}
#[must_use]
pub fn blend_mesh_indices(&self) -> Option<&MbmiChunk> {
self.blend_mesh_indices.as_ref()
}
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let file = File::create(path)?;
let mut writer = BufWriter::new(file);
serializer::serialize_to_writer(self, &mut writer)?;
writer.flush()?;
Ok(())
}
pub fn to_bytes(&self) -> Result<Vec<u8>> {
let mut buffer = Cursor::new(Vec::new());
serializer::serialize_to_writer(self, &mut buffer)?;
Ok(buffer.into_inner())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_minimal_built_adt() -> BuiltAdt {
BuiltAdt::new(
AdtVersion::VanillaEarly,
vec!["terrain/grass.blp".to_string()],
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
}
#[test]
fn test_new_built_adt() {
let adt = create_minimal_built_adt();
assert_eq!(adt.version(), AdtVersion::VanillaEarly);
assert_eq!(adt.textures().len(), 1);
assert_eq!(adt.textures()[0], "terrain/grass.blp");
}
#[test]
fn test_getters() {
let adt = create_minimal_built_adt();
assert_eq!(adt.version(), AdtVersion::VanillaEarly);
assert_eq!(adt.textures().len(), 1);
assert_eq!(adt.models().len(), 0);
assert_eq!(adt.wmos().len(), 0);
assert_eq!(adt.doodad_placements().len(), 0);
assert_eq!(adt.wmo_placements().len(), 0);
assert_eq!(adt.mcnk_chunks().len(), 0);
assert!(adt.flight_bounds().is_none());
assert!(adt.water_data().is_none());
assert!(adt.texture_amplifier().is_none());
assert!(adt.texture_params().is_none());
}
#[test]
fn test_to_bytes_now_works() {
let adt = create_minimal_built_adt();
let result = adt.to_bytes();
assert!(result.is_ok(), "Serialization should succeed");
let bytes = result.unwrap();
assert!(!bytes.is_empty(), "Serialized bytes should not be empty");
assert_eq!(&bytes[0..4], b"REVM");
}
#[test]
fn test_write_to_file_integration() {
let adt = create_minimal_built_adt();
let temp_dir = std::env::temp_dir();
let temp_path = temp_dir.join("test_adt_write.adt");
let result = adt.write_to_file(&temp_path);
assert!(result.is_ok(), "Write to file should succeed");
let metadata = std::fs::metadata(&temp_path);
assert!(metadata.is_ok(), "File should exist");
assert!(metadata.unwrap().len() > 0, "File should not be empty");
let _ = std::fs::remove_file(&temp_path);
}
}