rakata_formats/bwm/
writer.rs

1//! BWM binary writer.
2
3use std::io::{Cursor, Write};
4
5use super::{
6    binary::{write_f32, write_i32, write_u32},
7    checked_add, checked_mul, usize_to_u32, Bwm, BwmBinaryError, BwmVec3, AABB_ENTRY_SIZE,
8    ADJACENCY_ENTRY_SIZE, BWM_MAGIC, BWM_VERSION_V10, DISTANCE_ENTRY_SIZE, EDGE_ENTRY_SIZE,
9    FACE_INDEX_ENTRY_SIZE, FILE_HEADER_SIZE, MATERIAL_ENTRY_SIZE, NORMAL_ENTRY_SIZE,
10    VERTEX_ENTRY_SIZE,
11};
12
13/// Writes BWM data to a writer.
14#[cfg_attr(
15    feature = "tracing",
16    tracing::instrument(level = "debug", skip(writer, bwm))
17)]
18pub fn write_bwm<W: Write>(writer: &mut W, bwm: &Bwm) -> Result<(), BwmBinaryError> {
19    let vertex_count = bwm.vertices.len();
20    let face_count = bwm.faces.len();
21    let aabb_count = bwm.aabb_nodes.len();
22    let adjacency_count = bwm.adjacencies.len();
23    let edge_count = bwm.edges.len();
24    let perimeter_count = bwm.perimeters.len();
25
26    for (index, face) in bwm.faces.iter().enumerate() {
27        for (slot, vertex_index) in face.vertex_indices.iter().enumerate() {
28            let vertex_index = crate::binary::checked_to_usize(*vertex_index, "face_vertex_index")?;
29            if vertex_index >= vertex_count {
30                return Err(BwmBinaryError::InvalidData(format!(
31                    "faces[{index}].vertex_indices[{slot}] out of bounds for {vertex_count} vertices"
32                )));
33            }
34        }
35    }
36
37    // --- Compute contiguous table offsets (each table follows the previous) ---
38    let vertex_offset = FILE_HEADER_SIZE;
39    let vertex_size = checked_mul(vertex_count, VERTEX_ENTRY_SIZE, "vertex_size")?;
40    let face_indices_offset = checked_add(vertex_offset, vertex_size, "face_indices_offset")?;
41    let face_indices_size = checked_mul(face_count, FACE_INDEX_ENTRY_SIZE, "face_indices_size")?;
42    let materials_offset = checked_add(face_indices_offset, face_indices_size, "materials_offset")?;
43    let materials_size = checked_mul(face_count, MATERIAL_ENTRY_SIZE, "materials_size")?;
44    let normals_offset = checked_add(materials_offset, materials_size, "normals_offset")?;
45    let normals_size = checked_mul(face_count, NORMAL_ENTRY_SIZE, "normals_size")?;
46    let planar_distances_offset =
47        checked_add(normals_offset, normals_size, "planar_distances_offset")?;
48    let distances_size = checked_mul(face_count, DISTANCE_ENTRY_SIZE, "distances_size")?;
49    let aabb_offset = checked_add(planar_distances_offset, distances_size, "aabb_offset")?;
50    let aabb_size = checked_mul(aabb_count, AABB_ENTRY_SIZE, "aabb_size")?;
51    let adjacency_offset = checked_add(aabb_offset, aabb_size, "adjacency_offset")?;
52    let adjacency_size = checked_mul(adjacency_count, ADJACENCY_ENTRY_SIZE, "adjacency_size")?;
53    let edges_offset = checked_add(adjacency_offset, adjacency_size, "edges_offset")?;
54    let edge_size = checked_mul(edge_count, EDGE_ENTRY_SIZE, "edge_size")?;
55    let perimeters_offset = checked_add(edges_offset, edge_size, "perimeters_offset")?;
56
57    // --- Write header: magic + version (0x00..0x08) ---
58    writer.write_all(&BWM_MAGIC)?;
59    writer.write_all(&BWM_VERSION_V10)?;
60
61    // --- Write header: properties (0x08..0x48) ---
62    write_u32(writer, bwm.walkmesh_type.raw())?; // 0x08
63    write_vec3(writer, bwm.relative_hook1)?; // 0x0C
64    write_vec3(writer, bwm.relative_hook2)?; // 0x18
65    write_vec3(writer, bwm.absolute_hook1)?; // 0x24
66    write_vec3(writer, bwm.absolute_hook2)?; // 0x30
67    write_vec3(writer, bwm.position)?; // 0x3C
68
69    // --- Write header: table counts and offsets (0x48..0x88) ---
70    write_u32(writer, usize_to_u32(vertex_count, "vertex_count")?)?; // 0x48
71    write_u32(writer, usize_to_u32(vertex_offset, "vertex_offset")?)?; // 0x4C
72    write_u32(writer, usize_to_u32(face_count, "face_count")?)?; // 0x50
73    write_u32(
74        writer,
75        usize_to_u32(face_indices_offset, "face_indices_offset")?,
76    )?; // 0x54
77    write_u32(writer, usize_to_u32(materials_offset, "materials_offset")?)?; // 0x58
78    write_u32(writer, usize_to_u32(normals_offset, "normals_offset")?)?; // 0x5C
79    write_u32(
80        writer,
81        usize_to_u32(planar_distances_offset, "planar_distances_offset")?,
82    )?; // 0x60
83    write_u32(writer, usize_to_u32(aabb_count, "aabb_count")?)?; // 0x64
84    write_u32(writer, usize_to_u32(aabb_offset, "aabb_offset")?)?; // 0x68
85    write_u32(writer, bwm.unknown)?; // 0x6C
86    write_u32(writer, usize_to_u32(adjacency_count, "adjacency_count")?)?; // 0x70
87    write_u32(writer, usize_to_u32(adjacency_offset, "adjacency_offset")?)?; // 0x74
88    write_u32(writer, usize_to_u32(edge_count, "edge_count")?)?; // 0x78
89    write_u32(writer, usize_to_u32(edges_offset, "edges_offset")?)?; // 0x7C
90    write_u32(writer, usize_to_u32(perimeter_count, "perimeter_count")?)?; // 0x80
91    write_u32(
92        writer,
93        usize_to_u32(perimeters_offset, "perimeters_offset")?,
94    )?; // 0x84
95
96    // --- Write vertex table ---
97    for vertex in &bwm.vertices {
98        write_vec3(writer, *vertex)?;
99    }
100
101    // --- Write face tables (indices, materials, normals, planar distances) ---
102    for face in &bwm.faces {
103        write_u32(writer, face.vertex_indices[0])?;
104        write_u32(writer, face.vertex_indices[1])?;
105        write_u32(writer, face.vertex_indices[2])?;
106    }
107    for face in &bwm.faces {
108        write_u32(writer, face.material_id)?;
109    }
110    for face in &bwm.faces {
111        write_vec3(writer, face.normal)?;
112    }
113    for face in &bwm.faces {
114        write_f32(writer, face.planar_distance)?;
115    }
116
117    // --- Write AABB tree nodes ---
118    for node in &bwm.aabb_nodes {
119        write_vec3(writer, node.bb_min)?;
120        write_vec3(writer, node.bb_max)?;
121        write_u32(writer, node.face_index)?;
122        write_u32(writer, node.unknown)?;
123        write_u32(writer, node.split_axis)?;
124        write_u32(writer, node.left_child)?;
125        write_u32(writer, node.right_child)?;
126    }
127
128    // --- Write adjacency, edge, and perimeter tables ---
129    for adjacency in &bwm.adjacencies {
130        write_i32(writer, adjacency.edge_refs[0])?;
131        write_i32(writer, adjacency.edge_refs[1])?;
132        write_i32(writer, adjacency.edge_refs[2])?;
133    }
134    for edge in &bwm.edges {
135        write_i32(writer, edge.edge_index)?;
136        write_i32(writer, edge.transition)?;
137    }
138    for perimeter in &bwm.perimeters {
139        write_u32(writer, *perimeter)?;
140    }
141
142    Ok(())
143}
144
145/// Serializes BWM data to a byte vector.
146#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(bwm)))]
147pub fn write_bwm_to_vec(bwm: &Bwm) -> Result<Vec<u8>, BwmBinaryError> {
148    let mut cursor = Cursor::new(Vec::new());
149    write_bwm(&mut cursor, bwm)?;
150    Ok(cursor.into_inner())
151}
152
153fn write_vec3<W: Write>(writer: &mut W, value: BwmVec3) -> Result<(), BwmBinaryError> {
154    write_f32(writer, value.x)?;
155    write_f32(writer, value.y)?;
156    write_f32(writer, value.z)?;
157    Ok(())
158}