use crate::chunk_discovery::{ChunkDiscovery, ChunkLocation};
use crate::chunk_id::ChunkId;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AdtFileType {
Root,
Tex0,
Tex1,
Obj0,
Obj1,
Lod,
}
impl AdtFileType {
pub fn detect_from_chunks(chunks: &HashMap<ChunkId, Vec<ChunkLocation>>) -> Self {
let has_mcnk = chunks.contains_key(&ChunkId::MCNK);
let has_mhdr = chunks.contains_key(&ChunkId::MHDR);
let has_mtex = chunks.contains_key(&ChunkId::MTEX);
let has_mddf = chunks.contains_key(&ChunkId::MDDF);
let has_modf = chunks.contains_key(&ChunkId::MODF);
let has_mmdx = chunks.contains_key(&ChunkId::MMDX);
let has_mwmo = chunks.contains_key(&ChunkId::MWMO);
if has_mhdr && has_mcnk {
Self::Root
} else if has_mtex && !has_mhdr {
Self::Tex0
} else if (has_mddf || has_modf || has_mmdx || has_mwmo) && !has_mhdr {
Self::Obj0
} else if has_mcnk {
Self::Root
} else {
Self::Lod
}
}
pub fn from_discovery(discovery: &ChunkDiscovery) -> Self {
Self::detect_from_chunks(&discovery.chunks)
}
pub fn from_filename(filename: &str) -> Self {
let lower = filename.to_lowercase();
if lower.ends_with("_tex0.adt") {
Self::Tex0
} else if lower.ends_with("_tex1.adt") {
Self::Tex1
} else if lower.ends_with("_obj0.adt") {
Self::Obj0
} else if lower.ends_with("_obj1.adt") {
Self::Obj1
} else if lower.ends_with("_lod.adt") {
Self::Lod
} else {
Self::Root
}
}
#[must_use]
pub const fn description(&self) -> &'static str {
match self {
Self::Root => "Root ADT (terrain data)",
Self::Tex0 => "Texture file 0 (primary textures)",
Self::Tex1 => "Texture file 1 (additional textures)",
Self::Obj0 => "Object file 0 (M2/WMO placements)",
Self::Obj1 => "Object file 1 (additional objects)",
Self::Lod => "Level-of-detail file",
}
}
#[must_use]
pub const fn is_split_file(&self) -> bool {
!matches!(self, Self::Root)
}
#[must_use]
pub const fn has_terrain_geometry(&self) -> bool {
matches!(self, Self::Root | Self::Lod)
}
#[must_use]
pub const fn has_texture_data(&self) -> bool {
matches!(self, Self::Tex0 | Self::Tex1)
}
#[must_use]
pub const fn has_object_data(&self) -> bool {
matches!(self, Self::Obj0 | Self::Obj1)
}
}
impl std::fmt::Display for AdtFileType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.description())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detect_root_file_from_chunks() {
let mut chunks = HashMap::new();
chunks.insert(
ChunkId::MCNK,
vec![ChunkLocation {
offset: 1024,
size: 500,
}],
);
assert_eq!(AdtFileType::detect_from_chunks(&chunks), AdtFileType::Root);
}
#[test]
fn detect_texture_file_from_chunks() {
let mut chunks = HashMap::new();
chunks.insert(
ChunkId::MTEX,
vec![ChunkLocation {
offset: 512,
size: 200,
}],
);
assert_eq!(AdtFileType::detect_from_chunks(&chunks), AdtFileType::Tex0);
}
#[test]
fn detect_object_file_from_chunks() {
let mut chunks = HashMap::new();
chunks.insert(
ChunkId::MDDF,
vec![ChunkLocation {
offset: 2048,
size: 100,
}],
);
assert_eq!(AdtFileType::detect_from_chunks(&chunks), AdtFileType::Obj0);
}
#[test]
fn detect_lod_file_from_chunks() {
let chunks = HashMap::new();
assert_eq!(AdtFileType::detect_from_chunks(&chunks), AdtFileType::Lod);
}
#[test]
fn test_detect_from_discovery() {
let mut discovery = ChunkDiscovery::new(1000);
let mut chunks = HashMap::new();
chunks.insert(
ChunkId::MCNK,
vec![ChunkLocation {
offset: 100,
size: 500,
}],
);
discovery.chunks = chunks;
let file_type = AdtFileType::from_discovery(&discovery);
assert_eq!(file_type, AdtFileType::Root);
}
#[test]
fn test_detect_tex0_from_discovery() {
let mut discovery = ChunkDiscovery::new(1000);
let mut chunks = HashMap::new();
chunks.insert(
ChunkId::MTEX,
vec![ChunkLocation {
offset: 100,
size: 200,
}],
);
discovery.chunks = chunks;
let file_type = AdtFileType::from_discovery(&discovery);
assert_eq!(file_type, AdtFileType::Tex0);
}
#[test]
fn test_detect_obj0_from_discovery() {
let mut discovery = ChunkDiscovery::new(1000);
let mut chunks = HashMap::new();
chunks.insert(
ChunkId::MODF,
vec![ChunkLocation {
offset: 100,
size: 300,
}],
);
discovery.chunks = chunks;
let file_type = AdtFileType::from_discovery(&discovery);
assert_eq!(file_type, AdtFileType::Obj0);
}
#[test]
fn test_detect_lod_from_discovery() {
let discovery = ChunkDiscovery::new(1000);
let file_type = AdtFileType::from_discovery(&discovery);
assert_eq!(file_type, AdtFileType::Lod);
}
#[test]
fn detect_from_filename_tex0() {
assert_eq!(
AdtFileType::from_filename("Azeroth_32_48_tex0.adt"),
AdtFileType::Tex0
);
}
#[test]
fn detect_from_filename_case_insensitive() {
assert_eq!(
AdtFileType::from_filename("AZEROTH_32_48_TEX0.ADT"),
AdtFileType::Tex0
);
}
#[test]
fn detect_from_filename_root() {
assert_eq!(
AdtFileType::from_filename("Kalimdor_16_32.adt"),
AdtFileType::Root
);
}
#[test]
fn file_type_descriptions() {
assert_eq!(AdtFileType::Root.description(), "Root ADT (terrain data)");
assert_eq!(
AdtFileType::Tex0.description(),
"Texture file 0 (primary textures)"
);
}
#[test]
fn file_type_display() {
assert_eq!(format!("{}", AdtFileType::Root), "Root ADT (terrain data)");
}
#[test]
fn is_split_file() {
assert!(!AdtFileType::Root.is_split_file());
assert!(AdtFileType::Tex0.is_split_file());
assert!(AdtFileType::Obj0.is_split_file());
assert!(AdtFileType::Lod.is_split_file());
}
#[test]
fn has_terrain_geometry() {
assert!(AdtFileType::Root.has_terrain_geometry());
assert!(AdtFileType::Lod.has_terrain_geometry());
assert!(!AdtFileType::Tex0.has_terrain_geometry());
assert!(!AdtFileType::Obj0.has_terrain_geometry());
}
#[test]
fn has_texture_data() {
assert!(AdtFileType::Tex0.has_texture_data());
assert!(AdtFileType::Tex1.has_texture_data());
assert!(!AdtFileType::Root.has_texture_data());
assert!(!AdtFileType::Obj0.has_texture_data());
}
#[test]
fn has_object_data() {
assert!(AdtFileType::Obj0.has_object_data());
assert!(AdtFileType::Obj1.has_object_data());
assert!(!AdtFileType::Root.has_object_data());
assert!(!AdtFileType::Tex0.has_object_data());
}
}