use crate::api::{Obj0Adt, RootAdt, Tex0Adt};
use crate::error::{AdtError, Result};
pub fn merge_split_files(
mut root: RootAdt,
texture: Option<Tex0Adt>,
object: Option<Obj0Adt>,
) -> Result<RootAdt> {
if let Some(tex) = texture {
merge_texture_data(&mut root, tex)?;
}
if let Some(obj) = object {
merge_object_data(&mut root, obj)?;
}
Ok(root)
}
fn merge_texture_data(root: &mut RootAdt, texture: Tex0Adt) -> Result<()> {
root.textures = texture.textures;
root.texture_params = texture.texture_params;
for (i, tex_chunk) in texture.mcnk_textures.into_iter().enumerate() {
if i >= root.mcnk_chunks.len() {
return Err(AdtError::ChunkParseError {
chunk: crate::chunk_id::ChunkId::MCNK,
offset: 0,
details: format!(
"Texture MCNK index {} exceeds root MCNK count {}",
i,
root.mcnk_chunks.len()
),
});
}
if let Some(layers) = tex_chunk.layers {
root.mcnk_chunks[i].layers = Some(layers);
}
if let Some(alpha_maps) = tex_chunk.alpha_maps {
root.mcnk_chunks[i].alpha = Some(alpha_maps);
}
}
Ok(())
}
fn merge_object_data(root: &mut RootAdt, object: Obj0Adt) -> Result<()> {
root.models = object.models;
root.model_indices = object.model_indices;
root.wmos = object.wmos;
root.wmo_indices = object.wmo_indices;
root.doodad_placements = object.doodad_placements;
root.wmo_placements = object.wmo_placements;
for (i, obj_chunk) in object.mcnk_objects.into_iter().enumerate() {
if i >= root.mcnk_chunks.len() {
return Err(AdtError::ChunkParseError {
chunk: crate::chunk_id::ChunkId::MCNK,
offset: 0,
details: format!(
"Object MCNK index {} exceeds root MCNK count {}",
i,
root.mcnk_chunks.len()
),
});
}
if !obj_chunk.doodad_refs.is_empty() {
root.mcnk_chunks[i].doodad_refs = Some(crate::chunks::mcnk::McrdChunk {
doodad_refs: obj_chunk.doodad_refs,
});
}
if !obj_chunk.wmo_refs.is_empty() {
root.mcnk_chunks[i].wmo_refs = Some(crate::chunks::mcnk::McrwChunk {
wmo_refs: obj_chunk.wmo_refs,
});
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::{McnkChunkObject, McnkChunkTexture};
use crate::chunks::McnkChunk;
use crate::version::AdtVersion;
fn create_minimal_mcnk() -> McnkChunk {
unsafe { std::mem::zeroed() }
}
fn create_minimal_root() -> RootAdt {
RootAdt {
version: AdtVersion::Cataclysm,
mhdr: Default::default(),
mcin: Default::default(),
textures: vec![],
models: vec![],
model_indices: vec![],
wmos: vec![],
wmo_indices: vec![],
doodad_placements: vec![],
wmo_placements: vec![],
mcnk_chunks: vec![create_minimal_mcnk(), create_minimal_mcnk()],
flight_bounds: None,
water_data: None,
texture_flags: None,
texture_amplifier: None,
texture_params: None,
blend_mesh_headers: None,
blend_mesh_bounds: None,
blend_mesh_vertices: None,
blend_mesh_indices: None,
}
}
fn create_minimal_tex() -> Tex0Adt {
Tex0Adt {
version: AdtVersion::Cataclysm,
textures: vec!["texture1.blp".to_string(), "texture2.blp".to_string()],
texture_params: None,
mcnk_textures: vec![
McnkChunkTexture {
index: 0,
layers: None,
alpha_maps: None,
},
McnkChunkTexture {
index: 1,
layers: None,
alpha_maps: None,
},
],
}
}
fn create_minimal_obj() -> Obj0Adt {
Obj0Adt {
version: AdtVersion::Cataclysm,
models: vec!["model1.m2".to_string()],
model_indices: vec![0],
wmos: vec!["wmo1.wmo".to_string()],
wmo_indices: vec![0],
doodad_placements: vec![],
wmo_placements: vec![],
mcnk_objects: vec![
McnkChunkObject {
index: 0,
doodad_refs: vec![0, 1],
wmo_refs: vec![],
},
McnkChunkObject {
index: 1,
doodad_refs: vec![],
wmo_refs: vec![0],
},
],
}
}
#[test]
fn test_merge_split_files_complete() {
let root = create_minimal_root();
let tex = create_minimal_tex();
let obj = create_minimal_obj();
let result = merge_split_files(root, Some(tex), Some(obj));
assert!(result.is_ok());
let merged = result.unwrap();
assert_eq!(merged.textures.len(), 2);
assert_eq!(merged.models.len(), 1);
assert_eq!(merged.wmos.len(), 1);
assert_eq!(merged.mcnk_chunks.len(), 2);
assert!(merged.mcnk_chunks[0].doodad_refs.is_some());
assert_eq!(
merged.mcnk_chunks[0]
.doodad_refs
.as_ref()
.unwrap()
.doodad_refs
.len(),
2
);
assert!(merged.mcnk_chunks[1].wmo_refs.is_some());
assert_eq!(
merged.mcnk_chunks[1]
.wmo_refs
.as_ref()
.unwrap()
.wmo_refs
.len(),
1
);
}
#[test]
fn test_merge_split_files_texture_only() {
let root = create_minimal_root();
let tex = create_minimal_tex();
let result = merge_split_files(root, Some(tex), None);
assert!(result.is_ok());
let merged = result.unwrap();
assert_eq!(merged.textures.len(), 2);
assert_eq!(merged.models.len(), 0);
}
#[test]
fn test_merge_split_files_object_only() {
let root = create_minimal_root();
let obj = create_minimal_obj();
let result = merge_split_files(root, None, Some(obj));
assert!(result.is_ok());
let merged = result.unwrap();
assert_eq!(merged.textures.len(), 0);
assert_eq!(merged.models.len(), 1);
assert!(merged.mcnk_chunks[0].doodad_refs.is_some());
assert_eq!(
merged.mcnk_chunks[0]
.doodad_refs
.as_ref()
.unwrap()
.doodad_refs
.len(),
2
);
}
#[test]
fn test_merge_split_files_no_optional() {
let root = create_minimal_root();
let result = merge_split_files(root, None, None);
assert!(result.is_ok());
let merged = result.unwrap();
assert_eq!(merged.textures.len(), 0);
assert_eq!(merged.models.len(), 0);
}
#[test]
fn test_merge_texture_mcnk_count_mismatch() {
let root = create_minimal_root();
let mut tex = create_minimal_tex();
tex.mcnk_textures.push(McnkChunkTexture {
index: 2,
layers: None,
alpha_maps: None,
});
let result = merge_split_files(root, Some(tex), None);
assert!(result.is_err());
}
#[test]
fn test_merge_object_mcnk_count_mismatch() {
let root = create_minimal_root();
let mut obj = create_minimal_obj();
obj.mcnk_objects.push(McnkChunkObject {
index: 2,
doodad_refs: vec![],
wmo_refs: vec![],
});
let result = merge_split_files(root, None, Some(obj));
assert!(result.is_err());
}
}